/**********************
*  sQuery.js
*  version: 0.0.2
***********************/

// auto apply
$(function() {
    $('.squery').squeryRadio().squeryCheckbox().squerySelect();
    $(this).ajaxComplete(function() {
        $('.squery').squeryRadio().squeryCheckbox().squerySelect();
    });
});



/*****************************
 *
 *         R A D I O
 *
 *****************************/

var squeryRadioOn = 'squery-radio-on';

jQuery.fn.squeryRadio = function()
{
    this.filter(':radio').each(function()
    {
        // skip if already processed
        if ( this.className.match('squeried') ) return;

        // wrapper
        wrapper = document.createElement('div');
        wrapper.className = 'squery-radio-wrapper ' + this.className.replace(/\bsquery\b/g, '');

        // replacement
        replacement = document.createElement('div');
		replacement.tabIndex = 0;
        replacement.className = 'squery-radio-toggle' + ( this.checked ? ' ' + squeryRadioOn : '');
        replacement.targetSquery = this;
        replacement.onclick = squeryRadioCheck;

        this.squeryReplacement = replacement;

        $(this)
        .wrap(wrapper)
        .addClass('squeried')
        .after(replacement)
        .change(squeryRadioUpdate);
    });
    return this;
}
// check radio button
function squeryRadioCheck(event)
{
    $(event.target.targetSquery).check().change();
}
// onchange handler
function squeryRadioUpdate()
{
    // remove on from everybody
    /// @todo: DO I need this ? or do I let onchange on every radio take care of it's own?
    $(this).parents('form').find('.squery-radio-wrapper:has([name='+this.name+']) .squery-radio-toggle').removeClass(squeryRadioOn);
    // and put it back on event source
    $(this.squeryReplacement).addClass(squeryRadioOn);
}

/*****************************
 *
 *      C H E C K B O X
 *
 *****************************/

var squeryCheckboxOn = 'squery-checkbox-on';

jQuery.fn.squeryCheckbox = function()
{
    this.filter(':checkbox').each(function()
    {
        // skip if already processed
        if ( this.className.match('squeried') ) return;

        // get class name without "squery"
        _className = this.className.replace(/\bsquery\b/g, '');

        // wrapper
        wrapper = document.createElement('div');
        wrapper.className = 'squery-checkbox-wrapper ' + _className;

        // replacement
        replacement = document.createElement('div');
		replacement.tabIndex = 0;
		replacement.onkeydown = checkboxKeyboardHandler;
        replacement.className = 'squery-checkbox-toggle ' + ( this.checked ? squeryCheckboxOn : '');
        replacement.targetSquery = this;
        replacement.onclick = squeryCheckboxToggle;

        this.squeryReplacement = replacement;

        $(this)
        .wrap(wrapper)
        .addClass('squeried')
        .after(replacement)
        .change(squeryCheckboxUpdate);
    });
    return this;
}
// updates the real checkbox
function squeryCheckboxToggle(event)
{
    $(event.target.targetSquery).check(!($(event.target.targetSquery).checked())).change();
}
// onchange handler
function squeryCheckboxUpdate()
{
    $(this.squeryReplacement).condClass(this.checked, squeryCheckboxOn);
}
// keyboard event handler
function checkboxKeyboardHandler(event) {
    var checkbox = event.target.targetSquery;

	// up arrow
    switch (event.which) {
        case 32 : // space
		case 13 : // return
			checkbox.checked = !checkbox.checked;
			$(checkbox).change();
			return false;
	}
}


/*****************************
 *
 *        S E L E C T
 *   @todo: add the header
 ****************************/

var squerySelectOn = 'squery-select-on';
var squerySelectSettings = {
  innerHTML: 'v'
}
jQuery.fn.squerySelect = function(settings) {

    var settings = jQuery.extend(squerySelectSettings, settings || {});
    
    this.filter('select').each(function() {

        if ( this.className.match('squeried') ) return;

        // read class
        _className = this.className.replace(/\bsquery\b/g, '');

		// main elements
        wrapper = document.createElement('div');
		header = document.createElement('span');
        header.value = document.createElement('span');
        header.toggle = document.createElement('span');
        header.toggle.innerHTML = settings.innerHTML;
        ul = document.createElement('ul');
	
		// header	
		header.value.innerHTML = this.options[this.selectedIndex].innerHTML;
        header.appendChild(header.value);
        header.appendChild(header.toggle);
		header.tabIndex = 0;
        header.ul = header.toggle.ul = ul;
		header.onkeydown = header.toggle.onclick = function() { 
            this.ul.parentNode.style.display = 'block'; this.ul.focus(); 
        }
		this.header = header;

		// ul
		ul.tabIndex = 0;
        ul.onkeydown = selectKeyboardHandler;
		ul.onblur  = function() { this.parentNode.style.display = 'none'; }
        ul.select = this;

		// option -> li
        for (i = 0; i < this.length; i++) {
          li = document.createElement('li');
          li.select = this;
          li.option = this.options[i];
          li.innerHTML = this.options[i].innerHTML;
          li.index = i;
          li.onclick = squerySelectToggle;

          if (li.option.selected) li.className = squerySelectOn;

          this.options[i].li = li;
          this.options[i].firstChar = this.options[i].text.substr(0, 1).toLowerCase();

          ul.appendChild(li);
        }

	    // wrapper	
        wrapper.className = 'squery-select-wrapper ' + _className;

		// constructor
        $(this)
		.addClass('squeried')
        .wrap(wrapper)
        .after($('<div class="squery-options"/>').append(ul))
		.after(header)
        .change(squerySelectUpdate);

    });

    return this;
}

function squerySelectToggle(event) {
    select = event.target.select;
    if (select.multiple) {
        event.target.option.selected = !event.target.option.selected;
    } else {
        select.selectedIndex = event.target.index;
    }
    $(select).change();
}

function squerySelectUpdate() {
    for (i = 0; i < this.length; i++) {
        $(this.options[i].li).condClass(this.options[i].selected, squerySelectOn);
    }
	this.header.value.innerHTML = this.options[this.selectedIndex].innerHTML;
}


function selectKeyboardHandler(event) {

    select = event.target.select;

	// up arrow
    switch (event.which) {
        case 38 : // up
            select.selectedIndex = (select.selectedIndex + select.length - 1) % select.length;
            break;
        case 40 : // down
            select.selectedIndex = (select.selectedIndex + 1) % select.length;
            break;
        case 36 : // home
            select.selectedIndex = 0;
            break;
        case 35 : // end
            select.selectedIndex = select.length - 1;
            break;
        case  9 : // tab
        case 27 : // esc
        case 13 : // enter
            select.header.focus();
            break;
        default:
            // a..z
            if ( (event.which | 32) >= 97 && (event.which | 32) <= 122) {
                c = String.fromCharCode(event.which).toLowerCase();
                i = (select.selectedIndex + 1) % select.length;
                while ( i != select.selectedIndex) {
                    if ( c == select.options[i].firstChar ) {
                        select.selectedIndex = i;
                        break;
                    } else {
                        i = (i + 1) % select.length;
                    }
                }
            }
    }

    $(select).change();

    // no page scrolling
    if (event.which != 9 ) return false;
}


/*****************************
 *
 *  F O R M   T O O L B O X
 *
 *****************************/

// returns true of checkbox is checked
jQuery.fn.checked = function()
{
    return this[0].checked;
}
// chech checkbox unless check(false)
jQuery.fn.check = function(check)
{
    if (check !== false) {
        this.attr('checked', 'checked');
    } else {
        this.removeAttr('checked');
    }
    return this;
}
// uncheck a checkbox
jQuery.fn.uncheck = function()
{
    return this.check(false);
}

/******************************
 *
 *         S T R I N G
 *
 *****************************/
String.prototype.reverse = function() {
    return this.split('').reverse().join('');
}
/******************************
 *
 *          A R R A Y
 *
 *****************************/
// adds or removes a value from an array
Array.prototype.toggle = function(value) {
    if ( (index = jQuery.inArray(value, this)) != -1) {
      this.splice(index, 1);
    } else {
      this.push(value);
    }
    return this;
}
/******************************
 *
 *           C S S
 *
 *****************************/
jQuery.fn.hoverClass = function(className) {
    className = className || 'hover';
    this.hover(
        function() { $(this).addClass(className); },
        function() { $(this).removeClass(className); }
    );
    return this;
}

// depending on condition, adds or removes class className
jQuery.fn.condClass = function(condition, className) {
  if (condition)
    this.addClass(className);
  else
    this.removeClass(className);
}

